#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# This program is free software; you can redistribute it and/or modify  
# it under the terms of the GNU General Public License as published by  
# the Free Software Foundation; either version 2 of the License, or     
# (at your option) any later version.                                   
#                                                                         
# A copy of the license can be found in the license.txt file supplied   
# with this software or at: http://www.gnu.org/copyleft/gpl.html       
#


import select
import socket
import SocketServer
import threading

from url import URL

from endpoint import EndPoint
from callinlistener import CallInListener

from help import logging

from help.bencode import bencode as encode
from help.bencode import bdecode as decode


class   TcpEndPoint(EndPoint) :
    def __init__(self, url, listener) :
        super(TcpEndPoint, self).__init__(url, listener)

        self.tcp_thread = None
        self.tcp_server = None

        self.logger = logging.getLogger('OpenChord')
        ## __init__()


    class   RequestHandler(SocketServer.StreamRequestHandler) :
        def handle(self) :
            data = self.rfile.readline().strip()
            if not data : return
            meth,params = data.split('\t',1)
            method,parameters = self.pre_callin(meth, params)
            result = self.server.calliner.callin(method, parameters)
            response = self.post_callin(result)
            self.wfile.write(''.join((response, '\n')))
            ## handle()

        def pre_callin(self, method, parameters) :
            params = decode(parameters)
            return method,params
            ## pre_callin()

        def post_callin(self, response) :
            if response is None :
                response = 'None'
            return encode(response)
            ## post_callin()
        ## class RequestHandler

    class   Server(SocketServer.ThreadingMixIn, SocketServer.TCPServer) :
        def __init__(self, address, handler, calliner) :
            """Constructor.  May be extended, do not override."""
            SocketServer.TCPServer.__init__(self, address, handler)

            self.__is_shut_down = threading.Event()
            self.__serving = False

            self.calliner = calliner
            ## __init__()

        def serve_forever(self) :
            """Handle one request at a time until shutdown.

            Polls for shutdown every poll_interval seconds. Ignores
            self.timeout. If you need to do periodic tasks, do them in
            another thread.
            """
            poll_interval = 0.5
            self.__serving = True
            self.__is_shut_down.clear()
            while self.__serving:
                # XXX: Consider using another file descriptor or
                # connecting to the socket to wake this up instead of
                # polling. Polling reduces our responsiveness to a
                # shutdown request and wastes cpu at all other times.
                r, w, e = select.select([self], [], [], poll_interval)
                if r:
                    self._handle_request_noblock()
            self.__is_shut_down.set()
            ## serve_forever()

        def shutdown(self) :
            """Stops the serve_forever loop.

            Blocks until the loop has finished. This must be called while
            serve_forever() is running in another thread, or it will deadlock.
            """
            self.__serving = False
            self.__is_shut_down.wait()
            ## shutdown()

        def _handle_request_noblock(self):
            """Handle one request, without blocking.

            I assume that select.select has returned that the socket is
            readable before this function was called, so there should be
            no risk of blocking in get_request().
            """
            try:
                request, client_address = self.get_request()
            except socket.error:
                return
            if self.verify_request(request, client_address):
                try:
                    self.process_request(request, client_address)
                except:
                    self.handle_error(request, client_address)
                    self.close_request(request)
            ## _handle_request_noblock()

        ## class Server

    def open(self) :
        self.__class__.Server.allow_reuse_address = True
        self.tcp_server = self.__class__.Server(('', self.url.getPort()), self.__class__.RequestHandler, self.calliner)
        self.tcp_thread = threading.Thread(target=self.tcp_server.serve_forever)
        self.tcp_thread.start()
        ## open()

    def close(self) :
        if self.tcp_server :
            if self.logger.isDebugEnabled() : self.logger.debug('stop tcp server of TcpEndPoint "%s"', self.url.getURL())
            self.tcp_server.shutdown()
            self.tcp_server = None
        if self.tcp_thread :
            if self.logger.isDebugEnabled() : self.logger.debug('stop thread of TcpEndPoint "%s"', self.url.getURL())
            self.tcp_thread.join()
            self.tcp_thread = None
        if self.logger.isDebugEnabled() : self.logger.debug('close TcpEndPoint "%s"', self.url.getURL())
        ## close()

    ## class TcpEndPoint



